package com.ruoyi.base.utils.rsa;

import com.ruoyi.base.exception.SysException;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * RSA 加密
 *
 * @author lcf
 */
@Slf4j
public class RsaUtils {
    /**
     * 秘钥对
     */
    private static RsaPair rsaPair = createKeyPair();

    /**
     * 解密
     */
    public static String decode(String str) {
        try {
            return decrypt(str, rsaPair.getSecretKey());
        } catch (Exception e) {
            throw new SysException(e);
        }
    }

    /**
     * 加密
     */
    public static String encode(String str) {
        try {
            return encrypt(str, rsaPair.getPublicKey());
        } catch (Exception e) {
            throw new SysException(e);
        }
    }

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param publicKey 公钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    private static String encrypt(String str, String publicKey) throws Exception {
        //base64编码的公钥
        byte[] decoded = Base64.getUrlDecoder().decode(publicKey.getBytes(StandardCharsets.UTF_8));
        PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));

        return encrypt(str, pubKey);
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    private static String decrypt(String str, String privateKey) throws Exception {
        //base64编码的私钥
        byte[] decoded = Base64.getUrlDecoder().decode(privateKey.getBytes(StandardCharsets.UTF_8));
        PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));

        return decrypt(str, priKey);
    }

    /**
     * RSA公钥加密
     *
     * @param str       加密字符串
     * @param pubKey 公钥
     * @return 密文
     * @throws Exception 加密过程中的异常信息
     */
    private static String encrypt(String str, PublicKey pubKey) throws Exception {
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);

        return Base64.getUrlEncoder().encodeToString(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param priKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    private static String decrypt(String str, PrivateKey priKey) throws Exception {
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, priKey);

        return new String(cipher.doFinal(Base64.getUrlDecoder().decode(str.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);
    }

    /**
     * 随机生成密钥对
     */
    private static RsaPair createKeyPair() {
        try {
            // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
            // 初始化密钥对生成器，密钥大小为96-1024位
            keyPairGen.initialize(1024, new SecureRandom());
            // 生成一个密钥对，保存在keyPair中
            KeyPair keyPair = keyPairGen.generateKeyPair();
            // 得到私钥
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            // 得到公钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            // 得到公钥字符串
            String publicKeyString = new String(Base64.getUrlEncoder().encode(publicKey.getEncoded()), StandardCharsets.UTF_8);
            // 得到私钥字符串
            String privateKeyString = new String(Base64.getUrlEncoder().encode((privateKey.getEncoded())), StandardCharsets.UTF_8);

            return new RsaPair(publicKeyString, privateKeyString);
        } catch (NoSuchAlgorithmException e) {
            throw new SysException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        RsaPair pair = createKeyPair();
        String str = "URL变体修改的Base64";
        String result = encrypt(str, pair.getPublicKey());
        System.out.println(result);

        String source = decrypt(result, pair.getSecretKey());
        System.out.println(source);
    }
}
